package com.fineaiops.gateway.service;

import com.alibaba.fastjson.JSONObject;
import com.fineaiops.gateway.bean.Edge;
import com.fineaiops.gateway.bean.ServiceNode;
import com.fineaiops.gateway.service.mapper.ServiceNodeMapper;
import com.fineaiops.gateway.util.JsonBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;


@Service
public class NodeService {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    public void query() {
        System.out.println(jdbcTemplate.queryForList("select * from service_traffic"));
    }

    public List<ServiceNode> getAllServiceNode() {
        String sql = "SELECT t.id, t.name, ifnull(s.is_breakdown,0) as is_breakdown, ifnull(s.exp_score,0) as exp_score FROM service_status AS s RIGHT JOIN service_traffic AS t ON s.id=t.id";
        List<ServiceNode> res = jdbcTemplate.query(sql, new ServiceNodeMapper());
        return res;
    }
    public ServiceNode getServiceByID(String id) {
        String sql = "SELECT t.id, t.name, ifnull(s.is_breakdown,0) as is_breakdown, ifnull(s.exp_score,0) as exp_score FROM service_status AS s RIGHT JOIN service_traffic AS t ON s.id=t.id WHERE s.id = ?";
        try {
            ServiceNode res = jdbcTemplate.queryForObject(sql, new ServiceNodeMapper(), id);
            return res;
        } catch (Exception e) {
            System.out.println("get service by id exp : id: "+id);
            e.printStackTrace();
            return new ServiceNode(id, id, 1, 0);
        }
    }

    public List<JSONObject> getServiceEdges() {
        String sql = "SELECT t1.source, t1.dest, IFNULL(t2.status, 0) as st from ( " +
                "SELECT MAX(source_service_id) AS source, MAX(dest_service_id) AS dest, entity_id " +
                "FROM service_relation_server_side " +
                "GROUP BY entity_id) AS t1 LEFT JOIN server_exp_side AS t2 ON t1.entity_id = t2.entity_id ";
        RowMapper<JSONObject> mapper = (rs, rowNum) -> {
            JSONObject object = new JsonBuilder()
                    .put("source", rs.getString("source"))
                    .put("target", rs.getString("dest"))
                    .put("status", rs.getInt("st"))
                    .build();
            return object;
        };
        List<JSONObject> res = jdbcTemplate.query(sql, mapper);
        sql = "SELECT t1.source, t1.dest, IFNULL(t2.status, 0) as st from ( " +
                "SELECT MAX(source_service_id) AS source, MAX(dest_service_id) AS dest, entity_id " +
                "FROM service_relation_client_side " +
                "GROUP BY entity_id) AS t1 LEFT JOIN server_exp_side AS t2 ON t1.entity_id = t2.entity_id ";
        res.addAll(jdbcTemplate.query(sql, mapper));
        return res;
    }

    public TreeMap<String, ArrayList<String>> getRelatedService(String id) {
        TreeMap<String, ArrayList<String>> relationMap = new TreeMap<>();
        getRelatedService(id, relationMap, "source", "dest");
        getRelatedService(id, relationMap, "dest", "source");
        return relationMap;
    }

    public void getRelatedService(String id, TreeMap<String, ArrayList<String>> relationMap, String source, String dest) {
        relationMap.put(source, new ArrayList<>());
        String sourceColumn = source + "_service_id";
        String destColumn = dest + "_service_id";
        String sql1 = "SELECT distinct " + destColumn + " AS node " +
                "FROM service_relation_server_side " +
                "WHERE " + sourceColumn + " = ?";
        jdbcTemplate.query(sql1, rs -> {relationMap.get(source).add(rs.getString("node"));},
                id);
        String sql2 = "SELECT distinct " + destColumn + " AS node " +
                "FROM service_relation_client_side " +
                "WHERE " + sourceColumn + " = ?";
        jdbcTemplate.query(sql2, rs -> {relationMap.get(source).add(rs.getString("node"));},
                id);
    }

    public ArrayList<String> getLogTraceIDs(String id) {
        // todo 加上诊断时间
        String sql = "SELECT source FROM server_log_side WHERE target = ? order by score desc limit 1";
        ArrayList<String> ids = new ArrayList<>();
        String curID = id;
        try {
            for (int i = 0; i == 0 || curID != id; i++){
                ids.add(curID);
                curID = jdbcTemplate.queryForObject(sql, String.class, curID);
            }
        } catch (Exception e) {

        }
        return ids;
    }

    public JSONObject getLogInfluence(String id) {
        // todo 加上诊断时间
        String sql = "SELECT * FROM server_log_side WHERE source = ?";
        // 层次遍历
        Set<String> nodeSet = new HashSet<>();
        Queue<String> queue = new PriorityQueue<>();
        List<Edge> edges = new ArrayList<>();
        queue.add(id);
        nodeSet.add(id);
        while(!queue.isEmpty()) {
            String curID = queue.poll();
            jdbcTemplate.query(sql, rs -> {
                String target = rs.getString("target");
                queue.add(target);
                nodeSet.add(target);
                edges.add(new Edge(curID, target));
            }, curID);
        }
        List<ServiceNode> nodes = new ArrayList<>();
        for (String node: nodeSet) {
            nodes.add(new ServiceNode(node, "LOG-"+node, 0, 0));
        }
        return new JsonBuilder()
                .put("nodes", nodes)
                .put("edges", edges)
                .build();
    }

    public List<String> getOriginServiceByID(String id) {
        String sql = "SELECT distinct dest_service_id " +
                "as dest FROM service_relation_server_side " +
                "WHERE source_service_id = ?";
        List<String> res = new ArrayList<>();
        jdbcTemplate.query(sql, rs -> {
            res.add(rs.getString("dest"));
        }, id);
        return res;
    }

    public void updateLogSide(String sourceID, String targetID, double score) {
        String sql = "insert into server_log_side(entity_id, source, target, score) " +
                "values(?,?,?,?) " +
                "on duplicate key update " +
                "source=?, target=?, score=?";
        jdbcTemplate.update(sql,sourceID+"_"+targetID, sourceID, targetID, score, sourceID, targetID, score);
    }

    public List<Edge> getAllLogEdges() {
        String sql = "select * from server_log_side";
        List<Edge> res = new ArrayList<>();
        jdbcTemplate.query(sql, rs -> {
            res.add(new Edge(rs.getString("source"), rs.getString("target")));
        });
        return res;
    }

    public void updateServiceScore(String id, double score) {
        String sql = "insert into service_status(id, exp_score) values(?,?) on duplicate key update exp_score=?";
        long precent = Math.round(score * 100);
        jdbcTemplate.update(sql, id, precent, precent);
    }

    public void updateServiceSide(String souceService, String targetService) {
        String sql = "insert into server_exp_side(entity_id, status) values(?,1) on duplicate key update status=1";
        jdbcTemplate.update(sql, souceService+"-"+targetService);
    }

    public void saveTraceInfo(
            String timestamp,
            String serviceID,
            String spanID,
            String traceID,
            String duration,
            String type,
            String statusCode,
            String operationName,
            String parentSpan
    ) {
        String sql = "insert into trace_info(timestamp, service_id, span_id, trace_id, duration, type, status_code, operation_name, parent_span)" +
                "values(?,?,?,?,?,?,?,?,?) on duplicate key update timestamp=?";
        String rootSql = "insert into trace_info(timestamp, service_id, span_id, trace_id, duration, type, status_code, operation_name)" +
                "values(?,?,?,?,?,?,?,?) on duplicate key update timestamp=?";
        if(parentSpan == null) {
            jdbcTemplate.update(rootSql, timestamp, serviceID, spanID, traceID, duration, type, statusCode, operationName, timestamp);
            return;
        }
        jdbcTemplate.update(sql, timestamp, serviceID, spanID, traceID, duration, type, statusCode, operationName, parentSpan, timestamp);
    }

    public void convertTrace() {
        String sql = "select a.timestamp, a.service_id as target, b.service_id as source, a.parent_span from trace_info as a left join trace_info as b on a.parent_span = b.span_id";
        String adSql = "insert into service_relation_server_side (id, source_service_id, dest_service_id, entity_id, time_bucket) values (?,?,?,?,?)on duplicate key update time_bucket=?";
        AtomicInteger i = new AtomicInteger();
        jdbcTemplate.query(sql, rs -> {
            System.out.println("handle edge " + i);
            i.getAndIncrement();
            String source = rs.getString("source");
            String target = rs.getString("target");
            if (source==null || target == null || source.equals(target)) {
                return;
            }
            long timestamp = rs.getLong("timestamp");
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd");
            Date date = new Date(timestamp);
            String timeStr = simpleDateFormat.format(date);
            String entity = source + "-" + target;
            String id = timeStr + "_" + entity;
            jdbcTemplate.update(adSql, id, source, target, entity, timeStr, timeStr);
        });
    }

    public void syncNode() {
        String sqlSource = "select distinct source_service_id as id, time_bucket from service_relation_server_side";
        String sqlTarget = "select distinct dest_service_id as id, time_bucket from service_relation_server_side";
        String adSql = "insert into service_traffic (id, name, node_type, time_bucket) values (?,?,0,?) on duplicate key update time_bucket=?";
        AtomicInteger i = new AtomicInteger();
        RowCallbackHandler handler = rs -> {
            System.out.println("handle node " + i);
            i.getAndIncrement();
            String id = rs.getString("id");
            String time = rs.getString("time_bucket");
            jdbcTemplate.update(adSql, id, id, time, time);
        };
        jdbcTemplate.query(sqlSource, handler);
        jdbcTemplate.query(sqlTarget, handler);

    }

    public void updateBreakdownService(String service_id) {
        String sql = "insert into service_status (id,is_breakdown) values (?,1) on duplicate key update is_breakdown=1";
        jdbcTemplate.update(sql, service_id);

    }

    public List<String> getBreakdownLog() {
        String sql = "select distinct source from server_log_side where source not in (select target from server_log_side)";
        List<String> logID = new ArrayList<>();
        jdbcTemplate.query(sql, rs -> {
            logID.add(rs.getString("source"));
        });
        return logID;
    }

}
